package top.ikaros.vpn.whiteopen.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

/**
 * @program: white-open
 * @description: https://blog.csdn.net/weixin_44130166/article/details/88125419
 * //centos安装iptables：https://www.jianshu.com/p/bcd6d21ebbac
 * //iptables命令：https://www.cnblogs.com/xiujin/p/11494471.html
 * @author: Zed
 * @create: 2020-06-16 14:25
 **/
@Slf4j
@RestController
public class WhiteOpenController {

    @RequestMapping("close/all")
    public String closeAll(@RequestParam(defaultValue = "31895") int port) {
        //-A表示追加到过滤链的末尾
        List<String> close443 = operationRuleCmd("-A", null, 443, false);
        executeCmd(close443);
        List<String> closePort = operationRuleCmd("-A", null, port, false);
        executeCmd(closePort);
        return "success";
    }

    @RequestMapping("open/all")
    public String openAll(@RequestParam(defaultValue = "31895") int port) {
        List<String> open443 = operationRuleCmd("-D", null, 443, false);
        executeCmd(open443);
        List<String> openPort = operationRuleCmd("-D", null, port, false);
        executeCmd(openPort);
        return "success";
    }

    //iptables -F 删除所有现有规则
    //查看规则:iptables -L -n
    //iptables -I INPUT -p tcp --dport 22 -j ACCEPT
    //iptables -I INPUT -p tcp --dport 9090 -j ACCEPT #开启8000端口
    //对整个服务器(全部ip)禁止9100端口:iptables -D INPUT -p tcp --dport 31895 -j DROP
    //只允许10.10.10.250访问9100端口:iptables -I INPUT -s 119.137.53.32 -p tcp --dport 9100 -j ACCEPT
    //将规则保存到iptables中:service iptables save
    //重启iptables:service iptables restart
    //查看iptables 内容:cat iptables
    @RequestMapping("open")
    public String open(HttpServletRequest request, @RequestParam(defaultValue = "31895") int port) {
        String ipAddr = getIpAddr(request);
        delRule(ipAddr, 443, true);
        addRule(ipAddr, 443, true);
        delRule(ipAddr, port, true);
        addRule(ipAddr, port, true);
        //只要修改规则就生效 无需保存和重启
        //保存当前环境中未save的规则,只是把规则添加了,但没有save,不save的话,一旦重启iptables，这部分规则就丢失了
        saveIptables();
        /*restartIptables();*/
        return "success";
    }

    public void delRule(String ipAddr, int port, boolean acceptTFlag) {
        List<String> delCmd = operationRuleCmd("-D", ipAddr, port, acceptTFlag);
        executeCmd(delCmd);

    }

    public void addRule(String ipAddr, int port, boolean acceptTFlag) {
        //注意这里要用-I,才会加到过滤链最上面,过滤原则:满足就通过,不执行后面的过滤链了,所以得加到过滤链头
        List<String> addCmd = operationRuleCmd("-I", ipAddr, port, acceptTFlag);
        executeCmd(addCmd);
    }

    /**
     * @param operation   -A 新增,追加规则 -D删除规则 -C可以去重 -I可以追加到指定未知
     *                    按条件删除的话只需要把-A或-I替换成-D即可条件删除
     * @param ipAddr
     * @param port
     * @param acceptTFlag
     * @return
     */
    public List<String> operationRuleCmd(String operation, String ipAddr, int port, boolean acceptTFlag) {
        List<String> optCommand = new ArrayList<>();
        optCommand.add("iptables");
        optCommand.add(operation);
        optCommand.add("INPUT");
        if (ipAddr != null && !ipAddr.isEmpty()) {
            optCommand.add("-s");
            optCommand.add(ipAddr);
        }
        optCommand.add("-p");
        optCommand.add("tcp");
        optCommand.add("--dport");
        optCommand.add(String.valueOf(port));
        optCommand.add("-j");
        optCommand.add(acceptTFlag ? "ACCEPT" : "DROP");
        return optCommand;
    }

    /**
     * @param operation: save:保存 restart:重启 status:查看状态
     * @return
     */
    public List<String> operationIptablesCmd(String operation) {
        List<String> operationCommand = new ArrayList<>();
        operationCommand.add("service");
        operationCommand.add("iptables");
        operationCommand.add(operation);
        return operationCommand;
    }

    public void saveIptables() {
        List<String> saveCmd = operationIptablesCmd("save");
        executeCmd(saveCmd);
    }

    public void restartIptables() {
        List<String> saveCmd = operationIptablesCmd("restart");
        executeCmd(saveCmd);
    }

    private void executeCmd(List<String> saveCommand) {
        try {
            Process openProcess = new ProcessBuilder(saveCommand).redirectErrorStream(true).start();
            new PrintStream(openProcess.getInputStream()).start();
            // 加上这句,系统会等待转换完成.不加,就会在服务器后台自行转换.
            openProcess.waitFor();
        } catch (Exception e) {
            log.error("error={}", e.getMessage());
            e.printStackTrace();
        }
    }

    private String getIpAddr(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0
                    || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0
                    || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0
                    || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1")) {
                    // 根据网卡取本机配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    assert inet != null;
                    ipAddress = inet.getHostAddress();
                }
            }
            // 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress = "";
        }
        // ipAddress = this.getRequest().getRemoteAddr();
        return ipAddress;
    }
}
